An introduction to nilearn¶
import warnings
warnings.filterwarnings("ignore")
In this tutorial,
we’ll see how the Python library nilearn allows us to easily perform machine learning analyses with neuroimaging data,
specifically MRI and fMRI.
You may notice that the name nilearn is reminiscent of scikit-learn,
a popular Python library for machine learning.
This is no accident!
Nilearn and scikit-learn were created by the same team,
and nilearn is designed to bring machine LEARNing to the NeuroImaging (NI) domain.
With that in mind, let’s briefly consider why we might want specialized tools for working with neuroimaging data. When performing a machine learning analysis, our data often look something like this:
NB: update to Tal’s example once uploaded!
from sklearn import datasets
diabetes = datasets.load_diabetes(as_frame=True)
diabetes.data.head()
| age | sex | bmi | bp | s1 | s2 | s3 | s4 | s5 | s6 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.038076 | 0.050680 | 0.061696 | 0.021872 | -0.044223 | -0.034821 | -0.043401 | -0.002592 | 0.019908 | -0.017646 |
| 1 | -0.001882 | -0.044642 | -0.051474 | -0.026328 | -0.008449 | -0.019163 | 0.074412 | -0.039493 | -0.068330 | -0.092204 |
| 2 | 0.085299 | 0.050680 | 0.044451 | -0.005671 | -0.045599 | -0.034194 | -0.032356 | -0.002592 | 0.002864 | -0.025930 |
| 3 | -0.089063 | -0.044642 | -0.011595 | -0.036656 | 0.012191 | 0.024991 | -0.036038 | 0.034309 | 0.022692 | -0.009362 |
| 4 | 0.005383 | -0.044642 | -0.036385 | 0.021872 | 0.003935 | 0.015596 | 0.008142 | -0.002592 | -0.031991 | -0.046641 |
This data is clearly structured in a tabular format. This makes it easier to consider issues such as: which features would we like to predict? Or, how do we handle cross-validation?
Neuroimaging data¶
By contrast, neuroimaging data has a very different structure. Neuroimaging data has both spatial and temporal dependencies between successive data points. That is, knowing where and when something was measured tells you information about the surrounding data points. We also know that neuroimaging data contains a lot of noise that’s not blood-oxygen-level dependent (BOLD), such as head motion. Since we don’t think that these other noise sources are related to neuronal firing, we often need to consider how we can make sure that our analyses are not driven by these noise sources. These are all considerations that most machine learning software libraries are not designed to deal with! Nilearn therefore plays a crucial role in bringing machine learning concepts to the neuroimaging domain.
To get a sense of the problem, the quickest method is to just look at some neuroimaging data. You may have your own data locally that you’d like to work with. Nilearn also provides access to several neuroimaging data sets and atlases (we’ll talk about these a bit later). These data sets (and atlases) are only accessible because research groups chose to make their collected data publicly available. We owe them a huge thank you for this! The data set we’ll use today was originally collected by Rebecca Saxe’s group at MIT and hosted on OpenNeuro.
The nilearn team preprocessed the data set with fMRIPrep and downsampled it to a lower resolution, so it’d be easier to work with. We can learn a lot about this data set directly from the Nilearn documentation! For example, we can see that this data set contains over 150 children and adults watching a short Pixar film. Let’s download the first 30 participants.
from nilearn import datasets
development_dataset = datasets.fetch_development_fmri(n_subjects=30)
Dataset created in /home/runner/nilearn_data/development_fmri
Dataset created in /home/runner/nilearn_data/development_fmri/development_fmri
Downloading data from https://osf.io/yr3av/download ...
Downloaded 16041 of 16041 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3df4712b400183b7092/ ...
Downloaded 68285 of 68285 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e04712b400193b5bdf/ ...
Downloaded 5642698 of 5642698 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e14712b400183b7097/ ...
Downloaded 67851 of 67851 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e32286e80018c3e42c/ ...
Downloaded 4946416 of 4946416 bytes (100.0%, 0.0s remaining) ...done. (3 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e4a743a9001760814f/ ...
Downloaded 69170 of 69170 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e54712b400183b70a5/ ...
Downloaded 5875966 of 5875966 bytes (100.0%, 0.0s remaining) ...done. (3 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e52286e80018c3e439/ ...
Downloaded 68464 of 68464 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e72286e80017c41b3d/ ...
Downloaded 5845425 of 5845425 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e9a743a90017608158/ ...
Downloaded 68189 of 68189 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3e82286e80018c3e443/ ...
Downloaded 6522229 of 6522229 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3ea4712b400183b70b7/ ...
Downloaded 69463 of 69463 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3eb2286e80019c3c194/ ...
Downloaded 6139107 of 6139107 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff37da743a90018606df1/ ...
Downloaded 68468 of 68468 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff37c2286e80019c3c102/ ...
Downloaded 6293165 of 6293165 bytes (100.0%, 0.0s remaining) ...done. (3 seconds, 0 min)
Downloading data from https://osf.io/download/5cb4701e3992690018133d4f/ ...
Downloaded 68130 of 68130 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e6b353c58001b9cb34f/ ...
Downloaded 6136523 of 6136523 bytes (100.0%, 0.0s remaining) ...done. (3 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff37d4712b400193b5b54/ ...
Downloaded 68340 of 68340 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff37d4712b400183b7011/ ...
Downloaded 5837641 of 5837641 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff37e2286e80016c3c2cb/ ...
Downloaded 71331 of 71331 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3832286e80019c3c10f/ ...
Downloaded 6118892 of 6118892 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3822286e80018c3e37b/ ...
Downloaded 68533 of 68533 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff382a743a90018606df8/ ...
Downloaded 6453693 of 6453693 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3814712b4001a3b5561/ ...
Downloaded 68590 of 68590 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3832286e80016c3c2d1/ ...
Downloaded 6176491 of 6176491 bytes (100.0%, 0.0s remaining) ...done. (3 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3842286e80017c419e0/ ...
Downloaded 68630 of 68630 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3854712b4001a3b5568/ ...
Downloaded 6225801 of 6225801 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb4702f39926900171090ee/ ...
Downloaded 68331 of 68331 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e8b353c58001c9abe98/ ...
Downloaded 6232787 of 6232787 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3872286e80017c419ea/ ...
Downloaded 68425 of 68425 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3872286e80017c419e9/ ...
Downloaded 7361169 of 7361169 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3884712b400183b7023/ ...
Downloaded 68894 of 68894 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3884712b400193b5b5c/ ...
Downloaded 6586819 of 6586819 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff389a743a9001660a016/ ...
Downloaded 68848 of 68848 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38c2286e80016c3c2da/ ...
Downloaded 6111716 of 6111716 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38ca743a90018606dfe/ ...
Downloaded 68243 of 68243 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38ca743a9001760809e/ ...
Downloaded 6067448 of 6067448 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb47056353c58001c9ac064/ ...
Downloaded 69265 of 69265 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e5af2be3c001801f799/ ...
Downloaded 6122113 of 6122113 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb4703bf2be3c001801fa49/ ...
Downloaded 68479 of 68479 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e92a3bc970019f0717f/ ...
Downloaded 6295931 of 6295931 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38c4712b4001a3b5573/ ...
Downloaded 68301 of 68301 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38da743a900176080a2/ ...
Downloaded 6384587 of 6384587 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb47016a3bc970017efe44f/ ...
Downloaded 67912 of 67912 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e43f2be3c0017056b8a/ ...
Downloaded 5934622 of 5934622 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb470413992690018133d8c/ ...
Downloaded 68499 of 68499 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e9a353c58001c9abeac/ ...
Downloaded 6316470 of 6316470 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff38f2286e80018c3e38d/ ...
Downloaded 67733 of 67733 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3914712b4001a3b5579/ ...
Downloaded 6020576 of 6020576 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb4702a353c58001b9cb5ae/ ...
Downloaded 68275 of 68275 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e9b39926900190fad5c/ ...
Downloaded 6551822 of 6551822 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff391a743a900176080a9/ ...
Downloaded 68896 of 68896 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3914712b400173b5329/ ...
Downloaded 6116459 of 6116459 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb47023353c58001c9ac02b/ ...
Downloaded 68182 of 68182 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46eaa39926900160f69af/ ...
Downloaded 6752586 of 6752586 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3912286e80018c3e393/ ...
Downloaded 68403 of 68403 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min)
Downloading data from https://osf.io/download/5c8ff3952286e80017c41a1b/ ...
Downloaded 6023997 of 6023997 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb47045a3bc970019f073a0/ ...
Downloaded 68534 of 68534 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e913992690018133b1c/ ...
Downloaded 6312316 of 6312316 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb47052f2be3c0017057069/ ...
Downloaded 69162 of 69162 bytes (100.0%, 0.0s remaining) ...done. (2 seconds, 0 min)
Downloading data from https://osf.io/download/5cb46e5c353c5800199ac79f/ ...
Downloaded 6031594 of 6031594 bytes (100.0%, 0.0s remaining) ...done. (4 seconds, 0 min)
Now, this development_dataset object has several attributes which provide access to the relevant information.
For example, development_dataset.phenotypic provides access to information about the participants, such as whether they were children or adults.
We can use development_dataset.func to access the functional MRI (fMRI) data.
Let’s use the nibabel library to learn a little bit about this data:
import nibabel as nib
img = nib.load(development_dataset.func[0])
img.shape
(50, 59, 50, 168)
This means that there are 168 volumes, each with a 3D structure of (50, 59, 50).
Getting into the data: subsetting and viewing¶
Nilearn also provides many methods for plotting this kind of data.
For example, we can use nilearn.plotting.view_img to launch at interactive viewer.
Because each fMRI run is a 4D time series (three spatial dimensions plus time),
we’ll also need to subset the data when we plot it, so that we can look at a single 3D image.
Nilearn provides (at least) two ways to do this: with nilearn.image.index_img,
which allows us to index a particular frame–or several frames–of a time series,
and nilearn.image.mean_img,
which allows us to take the mean 3D image over time.
Putting these together, we can interatively view the mean image of the first participant using:
import matplotlib.pyplot as plt
from nilearn import image
from nilearn import plotting
mean_image = image.mean_img(development_dataset.func[0])
plotting.view_img(mean_image, threshold=None)
Extracting signal from fMRI volumes¶
As you can see, this data is decidedly not tabular! What we’d like is to extract and transform meaningful features from this data, and store it in a format that we can easily work with. Importantly, we could work with the full time series directly. But we often want to reduce the dimensionality of our data in a structured way. That is, we may only want to consider signal within certain learned or pre-defined regions of interest (ROIs), and when taking into account known sources of noise. To do this, we’ll use nilearn’s Masker objects. What are the masker objects ? First, let’s think about what masking fMRI data is doing:
Fig. 1 Masking fMRI data.¶
Essentially, we can imagine overlaying a 3D grid on an image. Then, our mask tells us which cubes or “voxels” (like 3D pixels) to sample from. Since our Nifti images are 4D files, we can’t overlay a single grid – instead, we use a series of 3D grids (one for each volume in the 4D file), so we can get a measurement for each voxel at each timepoint.
Masker objects allow us to apply these masks! To start, we need to define a mask (or masks) that we’d like to apply. This could correspond to one or many regions of interest. Nilearn provides methods to define your own functional parcellation (using clustering algorithms such as k-means), and it also provides access to other atlases that have previously been defined by researchers.
Choosing regions of interest¶
In this tutorial, we’ll use the MSDL (multi-subject dictionary learning; [1]) atlas, which defines a set of probabilistic ROIs across the brain.
import numpy as np
msdl_atlas = datasets.fetch_atlas_msdl()
msdl_coords = msdl_atlas.region_coords
n_regions = len(msdl_coords)
print(f'MSDL has {n_regions} ROIs, part of the following networks :\n{np.unique(msdl_atlas.networks)}.')
Dataset created in /home/runner/nilearn_data/msdl_atlas
Downloading data from https://team.inria.fr/parietal/files/2015/01/MSDL_rois.zip ...
MSDL has 39 ROIs, part of the following networks :
[b'Ant IPS' b'Aud' b'Basal' b'Cereb' b'Cing-Ins' b'D Att' b'DMN'
b'Dors PCC' b'L V Att' b'Language' b'Motor' b'Occ post' b'R V Att'
b'Salience' b'Striate' b'Temporal' b'Vis Sec'].
Downloaded 209734 of 209734 bytes (100.0%, 0.0s remaining) ...done. (1 seconds, 0 min) Extracting data from /home/runner/nilearn_data/msdl_atlas/5d25e157f36214b8ca9a12fd417aac1c/MSDL_rois.zip..... done.
Nilearn also provides us with an easy way to view this atlas directly:
plotting.plot_prob_atlas(msdl_atlas.maps)
<nilearn.plotting.displays.OrthoSlicer at 0x7fb2f44d9210>
A quick side-note on the NiftiMasker zoo¶
We’d like to supply these ROIs to a Masker object. All Masker objects share the same basic structure and functionality, but each is designed to work with a different kind of ROI.
The canonical nilearn.input_data.NiftiMasker works well if we want to apply a single mask to the data,
like a single region of interest.
But what if we actually have several ROIs that we’d like to apply to the data all at once?
If these ROIs are non-overlapping,
as in “hard” or deterministic parcellations,
then we can use nilearn.input_data.NiftiLabelsMasker.
Because we’re working with “soft” or probabilistic ROIs,
we can instead supply these ROIs to nilearn.input_data.NiftiMapsMasker.
For a full list of the available Masker objects, see the Nilearn documentation.
Applying a Masker object¶
We can supply our MSDL atlas-defined ROIs to the NiftiMapsMasker object,
along with resampling, filtering, and detrending parameters.
from nilearn import input_data
masker = input_data.NiftiMapsMasker(
msdl_atlas.maps, resampling_target="data",
t_r=2, detrend=True,
low_pass=0.1, high_pass=0.01).fit()
One thing you might notice from the above code is that immediately after defining the masker object,
we call the .fit method on it.
This method may look familiar if you’ve previously worked with scikit-learn estimators!
You’ll note that we’re not supplying any data to this .fit method;
that’s because we’re fitting the Masker to the provided ROIs, rather than to our data.
Dimensions, dimensions¶
We can use this fitted masker to transform our data.
roi_time_series = masker.transform(development_dataset.func[0])
roi_time_series.shape
(168, 39)
If you’ll remember, when we first looked at the data its original dimensions were (50, 59, 50, 168). Now, it has a shape of (168, 39). What happened?!
Rather than providing information on every voxel within our original 3D grid, we’re now only considering those voxels that fall in our 39 regions of interest provided by the MSDL atlas and aggregating across voxels within those ROIS. This reduces each 3D volume from a dimensionality of (50, 59, 50) to just 39, for our 39 provided ROIs.
You’ll also see that the “dimensions flipped;” that is, that we’ve transposed the matrix such that time is now the first rather than second dimension. This follows the scikit-learn convention that rows in a data matrix are samples, and columns in a data matrix are features.
Fig. 2 The scikit-learn conventions for feature and target matrices. From Jake VanderPlas’s Python Data Science Handbook.¶
One of the nice things about working with nilearn is that it will impose this convention for you, so you don’t accidentally flip your dimensions when using a scikit-learn model !
Creating and viewing a connectome¶
The simpler and most commonly used kind of connectivity is correlation.
It models the full (marginal) connectivity between pairwise ROIs.
We can estimate it using nilearn.connectome.ConnectivityMeasure.
from nilearn.connectome import ConnectivityMeasure
correlation_measure = ConnectivityMeasure(kind='correlation')
correlation_matrix = correlation_measure.fit_transform([roi_time_series])[0]
We can then plot this functional connectivity matrix:
np.fill_diagonal(correlation_matrix, 0)
plotting.plot_matrix(correlation_matrix, labels=msdl_atlas.labels,
vmax=0.8, vmin=-0.8, colorbar=True)
<matplotlib.image.AxesImage at 0x7fb2f3b53a50>
Or view it as an embedded connectome:
plotting.view_connectome(correlation_matrix, edge_threshold=0.2,
node_coords=msdl_atlas.region_coords)
Accounting for noise sources¶
They also allow us to perform other operations,
like correcting for measured signals of no interest (e.g., head motion).
Our development_dataset also includes several of these signals of no interest that were generated by fMRIPrep pre-processing.
We can access these with the confounds attribute,
using development_dataset.confounds.
Let’s quickly check what these look like for our first participant:
import pandas as pd
pd.read_table(development_dataset.confounds[0]).head()
| trans_x | trans_y | trans_z | rot_x | rot_y | rot_z | framewise_displacement | a_comp_cor_00 | a_comp_cor_01 | a_comp_cor_02 | a_comp_cor_03 | a_comp_cor_04 | a_comp_cor_05 | csf | white_matter | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -0.000233 | -0.076885 | 0.062321 | 0.000732 | 0.000352 | 0.000841 | 0.000000 | -0.099871 | -0.007286 | 0.001780 | -0.008073 | 0.030945 | -0.022393 | 439.699409 | 451.645460 |
| 1 | -0.006187 | -0.078395 | 0.056773 | 0.000112 | 0.000187 | 0.000775 | 0.055543 | -0.019437 | -0.042308 | 0.016735 | -0.012099 | 0.088777 | -0.006171 | 439.471640 | 451.103437 |
| 2 | -0.000227 | -0.069893 | 0.083102 | 0.000143 | 0.000364 | 0.000716 | 0.054112 | 0.009096 | -0.053206 | -0.030388 | -0.052925 | 0.019922 | 0.014776 | 439.744498 | 450.981505 |
| 3 | 0.002492 | -0.074707 | 0.060337 | 0.000202 | 0.000818 | 0.000681 | 0.057667 | 0.060195 | -0.083195 | 0.003578 | -0.037011 | 0.026946 | 0.002505 | 440.772620 | 450.600261 |
| 4 | -0.000226 | -0.084204 | 0.085079 | 0.000183 | 0.000548 | 0.000682 | 0.051438 | 0.049833 | -0.089819 | -0.020825 | -0.079329 | 0.008516 | -0.000938 | 440.115442 | 450.678959 |
We can see that there are several different kinds of noise sources included!
This is actually a subset of all possible fMRIPrep generated confounds that the Nilearn developers have pre-selected.
We could access the full list by passing the argument reduce_confounds=False to our original call downloading the development_dataset.
For most analyses, this list of confounds is reasonable, so we’ll use these Nilearn provided defaults.
For your own analyses, make sure to check which confounds you’re using!
corrected_roi_time_series = masker.transform(
development_dataset.func[0], confounds=development_dataset.confounds[0])
corrected_correlation_matrix = correlation_measure.fit_transform(
[corrected_roi_time_series])[0]
np.fill_diagonal(corrected_correlation_matrix, 0)
plotting.plot_matrix(corrected_correlation_matrix, labels=msdl_atlas.labels,
vmax=0.8, vmin=-0.8, colorbar=True)
<matplotlib.image.AxesImage at 0x7fb2f39bd6d0>
As before, we can also view this functional connectivity matrix as a connectome:
plotting.view_connectome(corrected_correlation_matrix, edge_threshold=0.2,
node_coords=msdl_atlas.region_coords)
In both the matrix and connectome forms, we can see a big difference when including the confounds! This is an important reminder to make sure that your data are cleaned of any possible sources of noise before running a machine learning analysis. Otherwise, you might be classifying participants on e.g. amount of head motion rather than a feature of interest!
- 1
Gael Varoquaux, Alexandre Gramfort, Fabian Pedregosa, Vincent Michel, and Bertrand Thirion. Multi-subject dictionary learning to segment an atlas of brain spontaneous activity. In Information Processing in Medical Imaging, 562–573. Springer Berlin Heidelberg, 2011.